var $ = window.$;
var jade = require('jade');
var animate = require('../../util/animate');
var event = require('../../core/event');
var uiUtil = require('../../util/ui');
var config = require('../../core/config');
var io = require('../io/ui');
var sound = require('../audio/sound');
var path = require('path');
var notify = require('../notify/ui');
var Log = require('../../util/logger');
var grid = require('../grid/manager');
var timer = require('../../core/timer');
var frac = require('../../util/frac');

var _inited = false;
var _autoBPM = null;
var _bpmSnap = [[1, 3], [2, 2.5], [10, 2], [100, 1.5]];

//实现ECMA的log1p函数
Math.log1p = Math.log1p || function(x){
	var t = 1 + x;
	if (t != 1)
    {   
        // 没错，是这样的，确实就是这样的
        // 不要试图改写这个公式
        return Math.log(t) * (x / (t - 1));
    }  
  	//x太小了使用泰勒展开式
    return x;
};

var ret = {
	init: function(){
		if(_inited){
			return;
		}
		this.lock = true;
		this.manual = false;
		this.audioFile = null;
		this.tapBeat = [];
		this.playId = -1;
		this.playCell = -1;
		this.bufTime = 0;
		this.currMetronome = -Infinity;
		this.disableMetronomeFor = 0;
		this.bpmVal = 0;
		this.offsetVal = 0;
		this.animateStick = null;
		this.timerId = timer.registUpdate(this.onUpdate.bind(this), 60);
		this.audioLatency = sound.fmod.getLatency();
		this.delayHandle = null;//用于在停止敲击T一段时间后结束测bpm
		this.lastHitTime = 0;//上次敲击T的时间
		sound.fmod.selectTrack(0);
		sound.fmod.pauseTrack();
		sound.fmod.selectTrack(1);
		this.metronomeAudio = sound.load('./static/sound/tock.wav');
		this.metronomeCell = sound.fmod.addCell();
		event.bindOwner('key', 'sample_timing', this);
		event.lock('key', 'sample_timing');

		_inited = true;
	},

	setManager: function(ma){
		this.manager = ma;
	},

	onUpdate: function(){
		sound.fmod.selectTrack(1);
		this.disableMetronomeFor -= 16;
		if (this.disableMetronomeFor > 0) {
			if (this.currMetronome != -Infinity) {
				this.currMetronome = -Infinity;
				sound.fmod.selectCell(this.metronomeCell);
				sound.fmod.offsetCell(-10000);
			}
			return;
		}
		var prevBufTime = this.bufTime;
		var time = sound.fmod.timeTrack();
		this.bufTime = sound.fmod.bufTimeTrack();
		if (this.bufTime > prevBufTime - 1 && time < this.currMetronome + 80) {
			return;
		}
		var bpm = this.bpmVal;
		var offset = this.offsetVal;
		if (!(bpm >= 50 && bpm <= 300 && offset >= -10000 && offset <= 1e7)) {
			if (this.currMetronome != -Infinity) {
				this.currMetronome = -Infinity;
				sound.fmod.selectCell(this.metronomeCell);
				sound.fmod.offsetCell(-10000);
			}
			return;
		}
		var beat = (this.bufTime - offset) * bpm / 60000;
		//通过计算拍号的奇偶来确定球的运动方向
		var direction = Math.ceil(beat) % 2;
		if(direction){
			this.animateStick.removeClass("move_left").addClass("move_right");
		}else{
			this.animateStick.addClass("move_left").removeClass("move_right");
		}
		this.currMetronome = Math.ceil(beat) * 60000 / bpm + offset;
		sound.fmod.selectCell(this.metronomeCell);
		sound.fmod.offsetCell(this.currMetronome);
	},

	onEvent: function(e, param){
		if(e == event.events.key.up){
			if(param.key == 27){
				this.hide();
				return event.result.stopAll;
			}
		}else if(e == event.events.key.down){
			if(param.key == 84){ // T
				this.onTap();
				return event.result.stopAll;
			}
		}
	},

	onTap: function () {
		if(!this.manual){
			return;
		}
		sound.fmod.selectTrack(1);
		if (!sound.fmod.isPlayingTrack()) {
			return;
		}
		var self = this;
		//若5秒钟没有再次敲击则停止测速
		if(!this.delayHandle){
			this.delayHandle = setInterval(function(){
				var now = new Date().getTime();
				if(self.lastHitTime && now - self.lastHitTime > 3000){
					clearInterval(self.delayHandle);
					self.lastHitTime = 0;
					self.manual = false;
					self.updateMusicParameter();
					self.parent.find("#dialog_timing_tip").hide();
					self.delayHandle = null;
				}
			},1000);
		}
		self.lastHitTime = new Date().getTime();
		var now = sound.fmod.timeTrack();
		var arr = this.tapBeat;
		arr.push(now);
		if(arr.length == 1){
		    //第一个是基准时间
			return;
		}
		//tap时间太大, 放弃之前的时间点
		//也就是不能测40bpm以下的
		if(now > arr[arr.length-1] + 1500 || now < arr[arr.length-1]){
			this.tapBeat = [now];
			this.parent.find("#dialog_timing_bpm").val("N/A");
			this.parent.find("#dialog_timing_offset").val("N/A");
			return;
		}
		if(arr.length < 8){
			return;
		}

		// 第一遍计算
		var i = 0;
		var n = arr.length;
		var sx = n * (n - 1) / 2;
		var sy = 0;
		var sxx = n * (n - 1) * (2 * n - 1) / 6;
		var sxy = 0;
		for (i = 0; i < n; i++) {
			sy += arr[i];
			sxy += i * arr[i];
		}
		var mx = sx / n;
		var my = sy / n;
		var lxx = sxx - sx * mx;
		var lxy = sxy - sx * my;
		var beatLen = lxy / lxx;
		var offset = my - beatLen * mx;

		// 按偏差排序
		var see = 0;
		var err = [];
		for (i = 0; i < n; i++) {
			var e = arr[i] - offset - beatLen * i;
			see += e * e;
			err.push([i, Math.abs(e)]);
		}
		var freedom = n - 2;
		var sd = Math.sqrt(see / freedom);
		err.sort(function (x, y) {
			return x[1] - y[1];
		});

		// 剔除偏差太大的 (Peirce's criterion)
		var maxOutliers = Math.floor(freedom * 0.4);
		var outliers = 0;
		var accept = false;
		for (outliers = 1; outliers <= maxOutliers; outliers++) {
			// 计算偏差阈值，牛顿法
			var thres = 1;
			var p = outliers / n;
			var q = outliers / (n - outliers);
			var r = outliers / (freedom - outliers);
			var dt = 1;
			do {
				// 近似计算 ∫_thres^+∞ e^(-(x^2-thres^2)/2)dx
				var k = 1 / (thres + (1 / (thres + 2 / (thres + 3 / (thres + 4 / (thres + 5 / (thres + 1.8)))))));
				var t2 = thres * thres;
				// sqrt(2/πe) ≈ 0.4839414490382867
				var ft = (Math.log1p(-p) - Math.log1p(-r * (t2 - 1)) * 0.5) / q - Math.log(0.4839414490382867 * k / p);
				var dfdt = (n - outliers) * thres / (freedom - outliers * t2) + (1 / k - thres);
				dt = ft / dfdt;
				thres -= dt;
			} while (Math.abs(dt) > 0.01);
			// 是否可以剔除这个
			if (err[n - outliers][1] < sd * thres) {
				accept = true;
				outliers--;
				break;
			}
		}
		if (!accept) {
			return;
		}

		// 第二遍计算
		if (outliers > 0) {
			n -= outliers;
			freedom -= outliers;
			sx = 0;
			sy = 0;
			sxx = 0;
			sxy = 0;
			var syy = 0;
			for (i = 0; i < n; i++) {
				var x = err[i][0];
				var y = arr[x];
				sx += x;
				sy += y;
				sxx += x * x;
				sxy += x * y;
				syy += y * y;
			}
			mx = sx / n;
			my = sy / n;
			lxx = sxx - sx * mx;
			lxy = sxy - sx * my;
			var lyy = syy - sy * my;
			beatLen = lxy / lxx;
			offset = my - beatLen * mx;
			sd = Math.sqrt(Math.max(-0, (lyy - lxy * beatLen) / freedom));
		}

		// 尝试舍入到整BPM
		if (sd > 100) {
			// TODO: 提示用户点击不准确
			return;
		}
		var sdBeatLen = sd / Math.sqrt(lxx);
		var bpm = 60000 / beatLen;
		var sdBpm = sdBeatLen / beatLen * bpm;
		for (i = 0;  i < _bpmSnap.length; i++) {
			var snappedBpm = Math.round(bpm * _bpmSnap[i][0]) / _bpmSnap[i][0];
			if (Math.abs(snappedBpm - bpm) < sdBpm * _bpmSnap[i][1]) {
				bpm = snappedBpm;
				beatLen = 60000 / bpm;
				offset = (sy - beatLen * sx) / n;
				break;
			}
		}

		// TODO: 用户可设置并且永久保存的自定义调节量
		offset = offset /* + userOffset */ - this.audioLatency;

		this.simplifyOffset(bpm, offset);
		this.disableMetronomeFor = 1500;
	},

	autoBPM: function() {
		//自动检测
		if(!this.audioFile){
			return;
		}
		if(!_autoBPM){
			_autoBPM = require('autotiming');
		}
		notify.showWaiting(true);
		var self = this;
		setTimeout(function(){
			var ret = _autoBPM.load(self.audioFile);
			if(ret == 0){
				notify.showWaiting(false);
				notify.addNotify({
					msg: config.i18n("err01")
				});
				return;
			}
			ret = _autoBPM.analyze();
			if(!ret || !ret.bpm){
				notify.showWaiting(false);
				notify.addNotify({
					msg: config.i18n("err02")
				});
				return;
			}
			self.simplifyOffset(ret.bpm, ret.offset);
			self.disableMetronomeFor = 0;
			notify.showWaiting(false);
			console.log(ret);
		}, 50); //给50ms给UI loading
	},

	simplifyOffset: function(bpm, offset){
		var noteOffset = -offset;
		if (noteOffset < 0) {
			var beatLen = 60000 / bpm;
			noteOffset = noteOffset % beatLen + beatLen;
		}
		/*var gridDiv = 4;
		var count = Math.round(noteOffset * bpm * gridDiv / 60000);
		var beat = Math.floor(count / gridDiv);
		var subbeat = count % gridDiv;
		noteOffset -= (beat + subbeat / gridDiv) * 60000 / bpm;*/
		this.parent.find("#dialog_timing_bpm").val(Math.round(bpm * 1e4) / 1e4);
		this.parent.find("#dialog_timing_offset").val(Math.round(noteOffset));
		//this.parent.find("#dialog_timing_beat").val((beat+1)+":"+subbeat+"/"+gridDiv);
	},

	getData: function() {
		var ret = {
			file: this.audioFile
		};
		ret.bpm = parseFloat(this.parent.find("#dialog_timing_bpm").val());
		ret.offset = parseInt(this.parent.find("#dialog_timing_offset").val());
		ret.beat = [0,0,4];
		if(!(ret.bpm > 0)){
			return null;
		}
		return ret;
	},

	stopMusic: function(release){
		sound.fmod.selectTrack(1);
		if(this.playId > -1){
			sound.fmod.pauseTrack();
		}
		this.tapBeat = [];
		if(release){
			sound.fmod.selectCell(this.playCell);
			sound.fmod.removeCell();
			//sound.unload(this.playId);
			this.playId = -1;
			this.playCell = -1;
		}else{
			sound.fmod.seekTrack(0);
		}
	},


	/**
	 *
	 * @param param 被选中的note的id
	 */
	show: function(param){
		this.param = param;
		if(this.parent){
			return;
		}
		var tmpl = jade.compileFile('./tmpl/sample/timing.jade');
		var html = tmpl({config: config});
		var self = this;
		$('#dialog_layer').append(html);
		this.parent = $('#dialog_timing');

		this.note = null;
		if(typeof param == 'undefined'){
			var _note = grid.getSelectNotes();
			if(_note.length){
				this.note = _note[0];
			}
		}else if(typeof param == 'number'){
			this.note = grid.getNote(param);
		}
		if(this.note && this.note.sound){
			this.audioFile = this.manager.getFilePath(this.note.sound);
			this.parent.find("#dialog_timing_title").html(this.note.sound);
			this.lock = false;
		}else if(param && param.file){
			this.audioFile = param.file;
			this.parent.find("#dialog_timing_title").html(path.basename(param.file));
			this.lock = false;
		}
		this.parent.on('click','.g_barhead i', function () {
			self.hide();
		});
		this.parent.on('click','.g_click', function(){
			var type = $(this).data('type');
			if(type == 'close'){
				self.hide();
				return;
			}else if(type == 'load'){
				io.getSoundPath().then(function (file) {
					if(!file || file == self.audioFile){
						return;
					}
					if(self.audioFile){
						self.stopMusic(true);
					}
					self.audioFile = file;
					self.parent.find("#dialog_timing_title").html(path.basename(file));
					self.lock = false;
				});
				return;
			}
			//下面的功能要在无锁状态下进行
			if(self.lock){
				return;
			}
			if(type == 'auto') {
				//self.parent.addClass("ext");
				//self.parent.find('.bpm').show();
				self.manual = false;
				self.parent.find("#dialog_timing_tip").hide();
				self.autoBPM();
			}else if(type == 'manu'){
				self.disableMetronomeFor = Infinity;
				self.tapBeat = [];
				self.parent.find("#dialog_timing_bpm").val("N/A");
				self.parent.find("#dialog_timing_offset").val("N/A");
				self.parent.find("#dialog_timing_tip").show();
				self.manual = true;
			}else if(type == 'play'){
				if(!self.audioFile){
					return;
				}
				self.updateMusicParameter();
				sound.fmod.selectTrack(1);
				if(self.playId != -1){
					sound.fmod.playTrack();
				}else{
					self.playId = sound.load(self.audioFile);
					if(self.playId == -1){
						notify.addNotify({
							msg: config.i18n("err01")
						});
						return;
					}
					sound.fmod.selectAudio(self.playId);
					self.playCell = sound.fmod.addCell();
					sound.fmod.offsetCell(0);
					sound.fmod.volumeCell(30);
					var len = sound.fmod.lengthAudio();
					sound.fmod.setLenTrack(len);
					sound.fmod.playTrack();
				}
			}else if(type == 'stop'){
				if(self.playId == -1){
					return;
				}
				self.stopMusic(false);
			}
		});

		this.parent.show();
		uiUtil.dialogInc();
		animate.normalScaleIn(this.parent).then(function(){
			//在文本框数值变化的时候刷新bpm和offset
			$("#dialog_timing_bpm,#dialog_timing_offset").blur(function(){
				self.updateMusicParameter();
			});
			self.animateStick = $("#dialog_timing_grid").children(".bar");
		});
		event.trigger(event.events.global.blur);
	},

	hide: function(){
		var self = this;
		//检查返回情况
		var data = this.getData();
		if(this.param && this.param.back){
			if(!data){
				notify.addNotify({
					msg: config.i18n('err08')
				});
				return;
			}
		}
		if(!this.parent){
			return;
		}
		timer.unregisterUpdate(this.timerId);
		if(this.playId != -1){
			this.stopMusic(true);
		}
		animate.normalScaleOut(this.parent).then(function(){
			self.parent.remove();
			self.parent = null;
			uiUtil.dialogDec();
			event.unbind(event.events.key.up, 'sample_timing');
			sound.fmod.selectTrack(0);
		});
		event.unlock();
		if(this.param && this.param.back){
			event.trigger(event.events.dialog.create, {sound: data});  //暂时只有create
		}else if(this.note){
			//写回note
			this.note.offset = data.offset;
			if(this.note.tid >= 0){
				var _timing = require('../timing/manager');
				var time = _timing.beatToTime(this.note.beat) + this.note.offset || 0;
				sound.offsetCell(this.note.tid, time);
			}
			event.trigger(event.events.note.change);
		}
		_inited = false;
	},

	updateMusicParameter : function(){
		var ret = this.getData();
		if (ret) {
			this.bpmVal = ret.bpm;
			this.offsetVal = -(ret.offset + frac.toFloat(ret.beat) * 60000 / ret.bpm);
		} else {
			this.bpmVal = NaN;
			this.offsetVal = NaN;
		}
		if(!isNaN(this.bpmVal)){
			this.animateStick.css("-webkit-transition-duration",60/this.bpmVal*0.8 + "s");
		}	
	}
};

module.exports = ret;